import ctypes
import json
import sys
from ctypes import *
# https://fisco-bcos-doc.readthedocs.io/zh_CN/latest/docs/develop/sdk/c_sdk/api.html
# 自定义回调上下文结构体，可以在里面再塞一些有需要的上下文内容
from client.local_lib_helper import LocalLibHelper
import queue


class BcosReqContext(Structure):
    _fields_ = [('seq', c_int),
                ('name', c_char_p),
                ('msg', c_char_p),
                ]

    def __init__(self, s, n, m):
        self.seq = s
        self.name = n.encode("utf-8")
        self.msg = m.encode("utf-8")


'''
//c语言定义的返回结构体
struct bcos_sdk_c_struct_response
{
    int error;   // 返回状态, 0成功, 其他失败
    char* desc;  // 失败时描述错误信息
    void* data;   // 返回数据, error=0 时有效
    size_t size;  // 返回数据大小, error=0 时有效
    void* context;  // 回调上下文,调用接口时传入的`context`参数
};
'''


# bcos sdk返回结构体
class BcosResponse(Structure):
    _fields_ = [('error', c_int),
                ('desc', c_char_p),
                ('data', c_void_p),
                ('size', c_size_t),
                ('context', c_void_p),
                ]

    def get_data_str(self):
        pool = create_string_buffer(self.size)
        memmove(pool, self.data, self.size)
        return str(pool, "utf-8")

    def get_desc(self):
        if self.desc is None:
            return ""
        return str(self.desc, "utf-8")

    def get_size(self):
        return self.size

    def get_error(self):
        return self.error

    def get_context(self):
        c = ctypes.cast(self.context, POINTER(BcosReqContext))
        return c.contents


# 模拟一个Future对象，用于接收回调，提供一个wait方法，把异步变成同步
class BcosCallbackFuture:
    queue = queue.Queue(1)
    is_timeout: False

    def bcos_callback(self, resp):
        # print(resp)
        self.size = resp.contents.get_size()
        pool = create_string_buffer(resp.contents.size)
        memmove(pool, resp.contents.data, resp.contents.size)
        self.data = str(pool, "utf-8")
        self.error = resp.contents.get_error()
        self.desc = resp.contents.get_desc()
        self.context = resp.contents.get_context()
        self.queue.put_nowait(1)

    def wait(self, timeout=5):
        self.is_timeout = False
        try:
            self.queue.get(True, timeout)
        except:
            self.is_timeout = True
            pass
        return self

    def display(self):
        if not hasattr(self, 'data'):
            print("empty")
            return
        # print("datastr:",datastr)
        if len(self.data) > 0:
            j = json.loads(self.data)
            print(json.dumps(j, indent=4))
        else:
            print(">> data is empty",)
        print(">> datasize:  ", self.size)
        print(">> error: ", self.error)
        print(">> desc:  ", self.desc)
        c = self.context
        print(">> context:({}),{},[{}]".format(c.seq, str(c.name, "utf-8"), str(c.msg, "utf-8")))


# bcos sdk回调函数定义
# typedef void (*bcos_sdk_c_struct_response_cb)(struct bcos_sdk_c_struct_response* resp)
BCOS_CALLBACK_FUNC = CFUNCTYPE(None, POINTER(BcosResponse))

# typedef void (*bcos_sdk_c_amop_subscribe_cb)(
# const char* endpoint, const char* seq, struct bcos_sdk_c_struct_response* resp);
BCOS_AMOP_SUB_CALLBACK_FUNC = CFUNCTYPE(None, c_char_p, c_char_p, POINTER(BcosResponse))


# 用dl load的方式封装接口，在windows、linux平台实测通过
class NativeBcos3sdk:
    # libname_win = 'bcos-c-sdk.dll'
    libname = "bcos-c-sdk"
    libpath = "bcos3sdklib"
    # libname_linux = "libbcos-c-sdk.so"
    # libpath_linux = "bcos3sdklib"
    nativelib = None

    def __init__(self, cfgfile):
        self.configfile = cfgfile
        locallib = LocalLibHelper(self.libname, self.libpath)
        self.nativelib = locallib.load_lib()
        print("init func")
        self.init_func()
        print("start sdk")
        self.start_sdk()

    # 初始化所有的方法，包括入参和出参定义
    def init_func(self):
        # print("init funcs in native lib", self.nativelib)
        self.nativelib.bcos_sdk_create_by_config_file.argtypes = [c_char_p]
        self.nativelib.bcos_sdk_create_by_config_file.restype = c_void_p
        self.bcos_sdk_create_by_config_file = self.nativelib.bcos_sdk_create_by_config_file
        self.nativelib.bcos_sdk_start.argtypes = [ctypes.c_void_p]
        self.bcos_sdk_start = self.nativelib.bcos_sdk_start
        self.nativelib.bcos_sdk_stop.argtypes = [c_void_p]
        self.bcos_sdk_stop = self.nativelib.bcos_sdk_stop
        self.nativelib.bcos_sdk_destroy.argtypes = [c_void_p]
        self.bcos_sdk_destroy = self.nativelib.bcos_sdk_destroy
        self.nativelib.bcos_sdk_get_last_error_msg.restype = ctypes.c_char_p
        self.bcos_sdk_get_last_error_msg = self.nativelib.bcos_sdk_get_last_error_msg
        self.bcos_sdk_get_last_error = self.nativelib.bcos_sdk_get_last_error

        # bcos_rpc_get_group_info(void* sdk, const char* group, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_group_info.argtypes = [
            c_void_p, c_char_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_group_info = self.nativelib.bcos_rpc_get_group_info
        # void bcos_rpc_get_block_by_number(void* sdk, const char* group, const char* node, int64_t block_number, int only_header, int only_tx_hash, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_block_by_number.argtypes = [c_void_p, c_char_p, c_char_p, c_long, c_uint, c_uint,
                                                                BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_block_by_number = self.nativelib.bcos_rpc_get_block_by_number
        # void bcos_rpc_get_block_by_hash(void * sdk, const  char * group, const char * node, const  char * block_hash, int only_header, int    only_tx_hash, bcos_sdk_c_struct_response_cb    callback, void * context)
        self.nativelib.bcos_rpc_get_block_by_hash.argtypes = [c_void_p, c_char_p, c_char_p, c_char_p, c_uint, c_uint,
                                                              BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_block_by_hash = self.nativelib.bcos_rpc_get_block_by_hash
        # void bcos_rpc_get_block_hash_by_number(void* sdk, const char* group, const char* node, int64_t block_number, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_block_hash_by_number.argtypes = [c_void_p, c_char_p, c_char_p, c_uint,
                                                                     BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_block_hash_by_number = self.nativelib.bcos_rpc_get_block_hash_by_number
        # --------
        self.nativelib.bcos_rpc_get_block_limit.argtypes = [c_void_p, c_char_p]
        self.nativelib.bcos_rpc_get_block_limit.restype = c_long
        self.bcos_rpc_get_block_limit = self.nativelib.bcos_rpc_get_block_limit

        # void bcos_rpc_get_block_number(void* sdk, const char* group, const char* node,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_block_number.argtypes = [
            c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_block_number = self.nativelib.bcos_rpc_get_block_number

        # void bcos_rpc_get_transaction(void* sdk, const char* group, const char* node, const char* tx_hash,int proof, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_transaction.argtypes = [c_void_p, c_char_p, c_char_p, c_char_p, c_int,
                                                            BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_transaction = self.nativelib.bcos_rpc_get_transaction
        # void bcos_rpc_get_transaction_receipt(void* sdk, const char* group, const char* node, const char* tx_hash, int proof, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_transaction_receipt.argtypes = [c_void_p, c_char_p, c_char_p, c_char_p, c_int,
                                                                    BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_transaction_receipt = self.nativelib.bcos_rpc_get_transaction_receipt

        # void bcos_rpc_get_code(void* sdk, const char* group, const char* node, const char* address,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_code.argtypes = [c_void_p, c_char_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC,
                                                     c_void_p]
        self.bcos_rpc_get_code = self.nativelib.bcos_rpc_get_code
        # void bcos_rpc_get_sealer_list(void* sdk, const char* group, const char* node,bcos_sdk_c_struct_response_cb callback, void* context
        self.nativelib.bcos_rpc_get_sealer_list.argtypes = [
            c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_sealer_list = self.nativelib.bcos_rpc_get_sealer_list
        # bcos_rpc_get_observer_list
        self.nativelib.bcos_rpc_get_observer_list.argtypes = [c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC,
                                                              c_void_p]
        self.bcos_rpc_get_observer_list = self.nativelib.bcos_rpc_get_observer_list
        # void bcos_rpc_get_pbft_view(void* sdk, const char* group, const char* node,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_pbft_view.argtypes = [
            c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_pbft_view = self.nativelib.bcos_rpc_get_pbft_view
        # void bcos_rpc_get_pending_tx_size(void* sdk, const char* group, const char* node,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_pending_tx_size.argtypes = [c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC,
                                                                c_void_p]
        self.bcos_rpc_get_pending_tx_size = self.nativelib.bcos_rpc_get_pending_tx_size
        # void bcos_rpc_get_sync_status(void* sdk, const char* group, const char* node,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_sync_status.argtypes = [
            c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_sync_status = self.nativelib.bcos_rpc_get_sync_status
        # void bcos_rpc_get_consensus_status(void* sdk, const char* group, const char* node,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_consensus_status.argtypes = [c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC,
                                                                 c_void_p]
        self.bcos_rpc_get_consensus_status = self.nativelib.bcos_rpc_get_consensus_status
        # void bcos_rpc_get_system_config_by_key(void* sdk, const char* group, const char* node,const char* key,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_system_config_by_key.argtypes = [c_void_p, c_char_p, c_char_p, c_char_p,
                                                                     BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_system_config_by_key = self.nativelib.bcos_rpc_get_system_config_by_key
        # void bcos_rpc_get_total_transaction_count(void* sdk, const char* group, const char* node, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_total_transaction_count.argtypes = [c_void_p, c_char_p, c_char_p,
                                                                        BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_total_transaction_count = self.nativelib.bcos_rpc_get_total_transaction_count
        # void bcos_rpc_get_group_peers(void* sdk, const char* group, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_group_peers.argtypes = [
            c_void_p, c_char_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_group_peers = self.nativelib.bcos_rpc_get_group_peers
        # void bcos_rpc_get_peers(void* sdk, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_peers.argtypes = [c_void_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_peers = self.nativelib.bcos_rpc_get_peers
        # void bcos_rpc_get_peers(void* sdk, const char* group, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_group_list.argtypes = [c_void_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_group_list = self.nativelib.bcos_rpc_get_group_list
        # void bcos_rpc_get_group_info(void* sdk, const char* group, bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_group_info.argtypes = [
            c_void_p, c_char_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_group_info = self.nativelib.bcos_rpc_get_group_info
        # void bcos_rpc_get_group_info_list(void* sdk,  bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_group_info_list.argtypes = [
            c_void_p, BCOS_CALLBACK_FUNC, c_void_p]
        self.bcos_rpc_get_group_info_list = self.nativelib.bcos_rpc_get_group_info_list
        # void bcos_rpc_get_group_node_info(void* sdk, const char* group, const char* node,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_rpc_get_group_node_info.argtypes = [c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC,
                                                                c_void_p]
        self.bcos_rpc_get_group_node_info = self.nativelib.bcos_rpc_get_group_node_info

        # event 类sdk
        # const char* bcos_event_sub_subscribe_event(void* sdk, const char* group, const char* params,bcos_sdk_c_struct_response_cb callback, void* context)
        self.nativelib.bcos_event_sub_subscribe_event.argtypes = [c_void_p, c_char_p, c_char_p, BCOS_CALLBACK_FUNC,
                                                                  c_void_p]
        self.nativelib.bcos_event_sub_subscribe_event.restype = c_char_p
        self.bcos_event_sub_subscribe_event = self.nativelib.bcos_event_sub_subscribe_event
        # void bcos_event_sub_unsubscribe_event(void* sdk, const char* id)
        self.nativelib.bcos_event_sub_unsubscribe_event.argtypes = [c_void_p, c_char_p]
        self.bcos_event_sub_unsubscribe_event = self.nativelib.bcos_event_sub_unsubscribe_event

        # AMOP类sdk
        # void bcos_amop_subscribe_topic(void* sdk, char** topics, size_t count)
        # 有个指针数组参数，有待测试
        self.nativelib.bcos_amop_subscribe_topic.argtypes = [c_void_p, POINTER(c_char_p), c_int]
        self.bcos_amop_subscribe_topic = self.nativelib.bcos_amop_subscribe_topic

        #  void bcos_amop_subscribe_topic_with_cb(void* sdk, const char* topic, bcos_sdk_c_amop_subscribe_cb cb, void* context)
        self.nativelib.bcos_amop_subscribe_topic_with_cb.argtypes = [c_void_p, c_char_p, BCOS_AMOP_SUB_CALLBACK_FUNC,
                                                                     c_void_p]
        self.bcos_amop_subscribe_topic_with_cb = self.nativelib.bcos_amop_subscribe_topic_with_cb

        # void bcos_amop_set_subscribe_topic_cb(void* sdk, bcos_sdk_c_amop_subscribe_cb cb, void* context)
        self.nativelib.bcos_amop_set_subscribe_topic_cb.argtypes = [c_void_p,  BCOS_AMOP_SUB_CALLBACK_FUNC,
                                                                    c_void_p]
        self.bcos_amop_set_subscribe_topic_cb = self.nativelib.bcos_amop_set_subscribe_topic_cb

        # void bcos_amop_unsubscribe_topic(void* sdk, char** topics, size_t count)
        self.nativelib.bcos_amop_unsubscribe_topic.argtypes = [c_void_p,  POINTER(c_char_p), c_long]
        self.bcos_amop_unsubscribe_topic = self.nativelib.bcos_amop_unsubscribe_topic

        # void bcos_amop_publish(void* sdk, const char* topic, void* data, size_t size, uint32_t timeout,bcos_sdk_c_amop_publish_cb cb, void* context)
        self.nativelib.bcos_amop_publish.argtypes = [
            c_void_p, c_char_p, c_long, c_int, BCOS_AMOP_SUB_CALLBACK_FUNC, c_void_p]
        self.bcos_amop_publish = self.nativelib.bcos_amop_publish

        # void bcos_amop_broadcast(void* sdk, const char* topic, void* data, size_t size)
        self.nativelib.bcos_amop_broadcast.argtypes = [c_void_p, c_char_p, c_char_p, c_long]
        self.bcos_amop_broadcast = self.nativelib.bcos_amop_broadcast

        # void bcos_amop_send_response(void* sdk, const char* peer, const char* seq, void* data, size_t size)
        self.nativelib.bcos_amop_send_response.argtypes = [
            c_void_p, c_char_p, c_char_p, c_char_p, c_long]
        self.bcos_amop_send_response = self.nativelib.bcos_amop_send_response

        # KeyPair 接口
        # void* bcos_sdk_create_keypair(int crypto_type); 1: ecdsa 2: sm
        self.nativelib.bcos_sdk_create_keypair.argtypes = [c_int]
        self.nativelib.bcos_sdk_create_keypair.restype = c_void_p
        self.bcos_sdk_create_keypair = self.nativelib.bcos_sdk_create_keypair

        # void* bcos_sdk_load_keypair(const char* pem_path);
        self.nativelib.bcos_sdk_load_keypair.argtypes = [c_char_p]
        self.nativelib.bcos_sdk_load_keypair.restype = c_void_p
        self.bcos_sdk_load_keypair = self.nativelib.bcos_sdk_load_keypair

        # void bcos_sdk_destroy_keypair(void* key_pair);
        self.nativelib.bcos_sdk_destroy_keypair.argtypes = [c_void_p]
        self.bcos_sdk_destroy_keypair = self.nativelib.bcos_sdk_destroy_keypair

        # const char* bcos_sdk_get_keypair_address(void* key_pair);
        self.nativelib.bcos_sdk_get_keypair_address.argtypes = [c_void_p]
        self.nativelib.bcos_sdk_get_keypair_address.restype = c_char_p
        self.bcos_sdk_get_keypair_address = self.nativelib.bcos_sdk_get_keypair_address

        # const char* bcos_sdk_get_keypair_public_key(void* key_pair);
        self.nativelib.bcos_sdk_get_keypair_public_key.argtypes = [c_void_p]
        self.nativelib.bcos_sdk_get_keypair_public_key.restype = c_char_p
        self.bcos_sdk_get_keypair_public_key = (self.nativelib.bcos_sdk_get_keypair_public_key)

        # const char* bcos_sdk_get_keypair_private_key(void* key_pair);
        self.nativelib.bcos_sdk_get_keypair_private_key.argtypes = [c_void_p]
        self.nativelib.bcos_sdk_get_keypair_private_key.restype = c_char_p
        self.bcos_sdk_get_keypair_private_key = (self.nativelib.bcos_sdk_get_keypair_private_key)

        # 编码交易
        # const char* bcos_sdk_create_signed_tx(void* key_pair, const char* to, const char* data, const char* chain_id, const char* group_id, int64_t block_limit);
        self.nativelib.bcos_sdk_create_signed_tx.argtypes = [c_void_p,c_char_p,c_char_p,c_char_p,c_char_p,c_int64,]
        self.nativelib.bcos_sdk_create_signed_tx.restype = c_char_p
        self.bcos_sdk_create_signed_tx = self.nativelib.bcos_sdk_create_signed_tx

        # 群组信息查询
        # void bcos_sdk_get_group_wasm_and_crypto(void* sdk, const char* group, int* wasm, int* sm_crypto);
        self.nativelib.bcos_sdk_get_group_wasm_and_crypto.argtypes = [c_void_p,c_char_p,POINTER(c_int),POINTER(c_int),]
        self.bcos_sdk_get_group_wasm_and_crypto = (self.nativelib.bcos_sdk_get_group_wasm_and_crypto)

        # const char* bcos_sdk_get_group_chain_id(void* sdk, const char* group);
        self.nativelib.bcos_sdk_get_group_chain_id.argtypes = [c_void_p, c_char_p]
        self.nativelib.bcos_sdk_get_group_chain_id.restype = c_char_p
        self.bcos_sdk_get_group_chain_id = self.nativelib.bcos_sdk_get_group_chain_id

    # 启动sdk

    def start_sdk(self):
        configfile = self.configfile
        if type(configfile) is str:
            configfile = configfile.encode("utf-8")
        print("create sdk by configfile:", configfile)
        self.sdk = self.bcos_sdk_create_by_config_file(configfile)
        # print("sdk is ",self.sdk)
        if self.sdk is None:
            # print(self.bcos_sdk_get_last_error_msg())
            sys.exit(-1)
        self.bcos_sdk_start(self.sdk)
